#include "nr_device_item_manager.h"
#include "nr_device_property.h"
#include "nr_util.h"

#define NR_DEVICE_ITEMS_FILE "\\DeviceItems.xml"

nr_device_item_manager::nr_device_item_manager(nr_device_type_manager& device_type_manager, 
    nr_io_manager& io_manager)
    : m_device_type_manager(device_type_manager)
    , m_io_manager(io_manager)
{
}

nr_device_item_manager::~nr_device_item_manager()
{
}

bool nr_device_item_manager::parse_device_item_server_parameters(QDomElement &node, nr_device_item_server &device_item_server)
{
    QDomNamedNodeMap attrs = node.attributes();

    Q_ASSERT(attrs.contains("id"));
    device_item_server.id() = attrs.namedItem("id").nodeValue();

    Q_ASSERT(attrs.contains("name"));
    device_item_server.name() = attrs.namedItem("name").nodeValue();

    Q_ASSERT(attrs.contains("type_id"));
    device_item_server.device_type() = m_device_type_manager.get_device_type(
        attrs.namedItem("type_id").nodeValue().toInt());

    return true;
}

bool nr_device_item_manager::parse_device_group_parameters(QDomElement &node, nr_device_group &device_group)
{
    QDomNamedNodeMap attrs = node.attributes();

    Q_ASSERT(attrs.contains("name"));
    device_group.name() = attrs.namedItem("name").nodeValue();

    return true;
}

bool nr_device_item_manager::parse_device_item_paramaters(QDomElement &node, nr_device_item &device_item)
{
    QDomNamedNodeMap attrs = node.attributes();

    Q_ASSERT(attrs.contains("id"));
    device_item.id() = attrs.namedItem("id").nodeValue();

    Q_ASSERT(attrs.contains("name"));
    device_item.name() = attrs.namedItem("name").nodeValue();

    Q_ASSERT(attrs.contains("type_id"));
    device_item.device_type() = m_device_type_manager.get_device_type(
        attrs.namedItem("type_id").nodeValue().toInt());

    return true;
}

bool nr_device_item_manager::parse_property_parameters(QDomElement &node, nr_device_property &device_property)
{
    QDomNamedNodeMap attrs = node.attributes();

    Q_ASSERT(attrs.contains("type_id"));
    device_property.id() = attrs.namedItem("type_id").nodeValue().toInt();
    device_property.property_type() = device_property.parse_property_type( node.tagName() );
    if ( device_property.property_type() == nr_device_property::Property)//Property
    {
        //QString text=node.toElement().text();
        device_property.value() = node.toElement().text();
    }

    return true;
}

bool nr_device_item_manager::analyze_property_node(QDomElement &property_node, nr_device_property &device_property)
{
    parse_property_parameters(property_node, device_property);

    QDomNode node = property_node.firstChild();
    while ( !node.isNull() )
    {
        QDomElement element = node.toElement();
        if ( element.tagName() == "PropertyGroup" || element.tagName() == "Property")
        {
            nr_device_property *new_device_property = new nr_device_property;
            analyze_property_node(element, *new_device_property);//recurrence
            device_property.children().insert(new_device_property->id(), new_device_property);
        }
        node = node.nextSibling();
    }
    
    return true;
}

bool nr_device_item_manager::analyze_device_item_node(QDomElement &device_item_node, nr_device_item &device_item)
{
    parse_device_item_paramaters(device_item_node, device_item);

    QDomNode node = device_item_node.firstChild();
    while ( !node.isNull() )
    {
        QDomElement element = node.toElement();
        if ( element.tagName() == "PropertyGroup" || element.tagName() == "Property")
        {
            nr_device_property *new_device_property = new nr_device_property;
            analyze_property_node(element, *new_device_property);
            device_item.properties().insert(new_device_property->id(), new_device_property);
        }
        else if ( element.tagName() == "DeviceItem" )
        {
            nr_device_item *new_device_item = new nr_device_item;
            analyze_device_item_node(element, *new_device_item);//recurrence
            new_device_item->parent_item() = &device_item;//add parent_item
            device_item.items().insert(new_device_item->id(), new_device_item);
        }

        node = node.nextSibling();
    }

    return true;
}

bool nr_device_item_manager::analyze_device_group_node(QDomElement &device_group_node, nr_device_group &device_group, nr_device_item &parent_device_item)
{
    parse_device_group_parameters(device_group_node, device_group);

    QDomNode node = device_group_node.firstChild();
    while ( !node.isNull() )
    {
        QDomElement element = node.toElement();
        if ( element.tagName() == "DeviceItem" )
        {
            nr_device_item *new_device_item = new nr_device_item;
            analyze_device_item_node(element, *new_device_item);
            new_device_item->parent_item() = &parent_device_item;//parent_item
            device_group.items().insert(new_device_item->id(), new_device_item);
        }
        else if ( element.tagName() == "DeviceGroup" )
        {
            nr_device_group *new_device_group = new nr_device_group;
            analyze_device_group_node(element, *new_device_group, parent_device_item);//recurrence
            device_group.groups().insert(new_device_group->name(), new_device_group);
        }

        node = node.nextSibling();
    }

    return true;
}

nr_error_t nr_device_item_manager::load()
{
    // Load from the xml current now, then should load from the database
    QString xml_path = nr_util::get_install_dir() + QString(NR_DEVICE_ITEMS_FILE);
    QFile file(xml_path);
    if (!file.open(QIODevice::ReadOnly))
    {
        return NR_ERROR_OPEN_FILE_FAILED;
    }

    QString error_msg;
    int error_line = 0, error_column = 0; 
    QDomDocument doc;
    if (!doc.setContent(&file, &error_msg, &error_line, &error_column))
    {
        return NR_ERROR_PARSE_XML_FAILED;
    }

    QDomElement device_item_server_node = doc.namedItem("DeviceItemServer").toElement();
    parse_device_item_server_parameters(device_item_server_node, this->root_item());
    
    QDomNodeList device_group_list = device_item_server_node.childNodes();
    for(int i = 0 ; i < device_group_list.count(); ++i)
    {
        nr_device_group *device_group = new nr_device_group;
        bool ret = analyze_device_group_node(device_group_list.item(i).toElement(), *device_group, this->root_item());
        Q_ASSERT(ret);
        this->root_item().groups().insert(device_group->name(), device_group);
    }

    return NR_ERROR_NONE;
}

nr_error_t nr_device_item_manager::unload()
{
    return NR_ERROR_NOT_IMPLEMENTED;
}

nr_device_item* nr_device_item_manager::find_device_item_by_id(const nr_device_id_t &device_id)
{
    return m_device_item_server.find_device_item_by_id (device_id);
}

static bool create_attributte_to_element(QDomDocument &doc, QDomElement &node, const QString &attr_name, QString &attr_value)
{
    QDomAttr domattr_para = doc.createAttribute(attr_name);
    domattr_para.setValue( attr_value );
    node.setAttributeNode( domattr_para );
    return true;
}

//create node with PropertyGroup tag and Property tag
static bool create_node_property(QDomDocument &doc, QDomElement &node, nr_device_property &device_property)
{
    create_attributte_to_element(doc, node, "type_id", QString::number(device_property.id()));
    QString nodeText_str = device_property.value().toString();
    if ( nodeText_str != "" )
    {
        QDomText node_text = doc.createTextNode( nodeText_str );
        node.appendChild( node_text );
    }

    foreach(nr_device_property* prop, device_property.children())
    {
        QString tag_name = prop->property_type_to_string();
        QDomElement property_node = doc.createElement(tag_name);
        create_node_property(doc, property_node, *prop);
        
        node.appendChild(property_node);
    }

    return true;
}

static bool create_node_device_item(QDomDocument &doc, QDomElement &node, nr_device_item &device_item)
{
    create_attributte_to_element(doc, node, "id", device_item.id());
    create_attributte_to_element(doc, node, "name", device_item.name());
    create_attributte_to_element(doc, node, "type_id", QString::number(device_item.device_type()->id()));

    //QHashIterator<nr_id_t, nr_device_property*> device_properties_iterator( device_item.properties() );
    
    foreach(nr_device_property* prop, device_item.properties())
    {
        QString tagName = prop->property_type_to_string();
        QDomElement property_node =  doc.createElement( tagName );
        create_node_property(doc, property_node, *prop/**device_properties_iterator.value()*/);

        node.appendChild(property_node);
    }

    foreach(nr_device_item* item, device_item.items())
    {
        QDomElement device_item_node = doc.createElement("DeviceItem");
        create_node_device_item(doc, device_item_node, *item);
        node.appendChild(device_item_node);
    }

    return true;
}

static bool create_node_device_group(QDomDocument &doc, QDomElement &node, nr_device_group &device_group)
{
    create_attributte_to_element(doc, node, "name", device_group.name());

    foreach(nr_device_group* group, device_group.groups())
    {

        QDomElement device_group_node = doc.createElement("DeviceGroup");
        create_node_device_group(doc, device_group_node, *group);
        node.appendChild(device_group_node);
    }
    
    foreach(nr_device_item* item, device_group.items())
    {
        QDomElement device_item_node = doc.createElement("DeviceItem");
        create_node_device_item(doc, device_item_node, *item);
        
        node.appendChild(device_item_node);
    }
    
    return true;
}

static bool create_node_device_item_server(QDomDocument &doc,nr_device_item_server &device_item_server)
{
    QDomElement root = doc.createElement("DeviceItemServer");
    
    create_attributte_to_element(doc, root, "id", device_item_server.id());
    create_attributte_to_element(doc, root, "name", device_item_server.name());
    create_attributte_to_element(doc, root, "type_id", QString::number(device_item_server.device_type()->id()) );
        
    foreach(nr_device_group* device_group, device_item_server.groups())
    {
        QDomElement deviceGroup_node = doc.createElement("DeviceGroup");
        create_node_device_group(doc, deviceGroup_node, *device_group);

        root.appendChild(deviceGroup_node);
    }

    doc.appendChild(root);
    return true;
}

nr_error_t nr_device_item_manager::save_as_xml( const QString& xml_file )
{
    QFile file(xml_file);
    if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
    {
        return NR_ERROR_OPEN_FILE_FAILED;
    }

    QDomDocument doc;
    QDomText text;
    QDomElement element;
    QDomProcessingInstruction instruction;
    instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"utf-8\"");
    doc.appendChild(instruction);

    create_node_device_item_server(doc, this->root_item());

    QTextStream out(&file);
    doc.save(out,4);
    return NR_ERROR_NONE;
    
}